import { uuid } from "./utils/uuid";
import { GraphNodeLayout } from "./graph_node_layout";
import { Event } from "./utils/Event";
import { LanguageAnalysis } from "./language_analysis";
import { reactive, toRaw, } from "vue";

class Group{
    event:Event;
    codeType = "js";
    uuid = uuid();
    constructor(){
        this.event = new Event(this);
    }

    static get getEventName(){
        return ["update"];
    }

    setCodeType(type="js"){
        this.codeType = type;
    }

    toString(){
        return ""
    }
}

type ObjectType = {[k:string]:any};

export class FunGroup extends Group{
    private funName:string;
    private data:{[k:string]:any} | null = null;
    private graph: GraphNodeLayout|null = null;
    type = "fun";
    mode = "fun";

    constructor(funName=""){
       super();
        this.funName = funName;
    }

    setData(data:{[k:string]:any}){
        this.data = data.content;
    }

    setGraph(graph:GraphNodeLayout){
        this.graph = graph;
    }

    setFunName(name:string){
        this.funName = name;
    }

    getCodeType(){
        return this.codeType;
    }

    setMode(mode="fun"){
        this.mode = mode;
    }

    getData(){
        return {
            type:"fun",
            name: this.funName,
            content: this.data
        };
    }

    resave(){
        this.data = this.getGraph().export();
    }

    getGraph(){
        const that = toRaw(this);
        if(!that.graph){
            that.graph = new GraphNodeLayout();
            if(that.data) that.graph.import(that.data);
        }

        return that.graph;
    }

    getFunName(){
        return this.funName;
    }

    toString() {
        if(this.getGraph() && this.graph){
            const token = this.graph.toToken();
            let code = new LanguageAnalysis().analysis(token,this.codeType) || "";
            
            if(this.funName.trim()!=""){
                if(this.mode == "fun") {
                    code = code.replace("function","function "+this.funName);
                }
                else if(this.mode == "class") code = code.replace("function",this.funName);
            }
            return code;
        }
        return "";
    }
}

export class ClassGroup extends Group{
    className:string;
    funArray:({[k:string]:any})[] = reactive([]);
    propArray:string[] = reactive([]);
    funCount = 0;
    type = "class";
    extendsName:string|null = "Object";

    constructor(className=""){
       super();
        this.className = className;
    }

    addEmptyProp(){
        this.propArray.push("prop"+this.funCount++);
    }

    addEmptyFun(){
        const f = new FunGroup("fun"+this.funCount++);
        this.funArray.push(f);
    }

    addFun(fun:FunGroup | ObjectType){
        
        if(fun instanceof FunGroup){
            if(this.isName(fun.getFunName())) return false;
            this.funArray.push(fun);
            return true;
        }
        else{
            if(this.isName(fun.name)) return false;
            const f = new FunGroup(fun.name);
            f.setData(fun);
            this.funArray.push(f);
            return true;
        }
    }

    addProp(){return}

    isName(name:string){
        for (let i = 0; i < this.funArray.length; i++) {
            const f = this.funArray[i];
           if(f.getFunName() === name) return true;
        }
        return false;
    }

    setClassName(name:string){
        this.className = name;
    }

    setExtendsName(name:string){
        this.extendsName = name;
    }

    getData(){
        this.resave();
        const data:ObjectType = {
            type:"class",
            name: this.className,
            funCount: this.funCount,
            funs: [],
            props: []
        };

        const funArray = toRaw(this.funArray);
        funArray.forEach(item=>{
            if(item instanceof FunGroup){
                data.funs.push(item.getData());
            }
        })

        const propArray = toRaw(this.propArray);
        propArray.forEach(item=>{
            data.props.push(item);
        })
        
        return data;
    }

    setData(data:ObjectType){ 
        if(data.funs){
            this.className = data.name;
            this.funCount = data.funCount;
            this.propArray.push(...data.props);
            data.funs.forEach((item:ObjectType)=>{
                this.addFun(item);
            });
        }
    }

    resave(){
        this.funArray.forEach(item=>{
            if(item instanceof FunGroup){
                item.resave();
            }
        })
    }

    getClassName(){
        return this.className;
    }

    toString(): string {
        let code = `class ${this.className}`;
        if(this.extendsName && this.extendsName.trim() != "" && this.extendsName!="Object"){
            code += ` extends ${this.extendsName}`
        }

        code += "{"

        this.propArray.forEach(prop=>{
            code += ` ${prop};`;
        })

        this.funArray.forEach(fun=>{
            fun.setMode("class");
            code += fun.toString();
        })

        return code += "}"
    }
}

export class PackGroup extends Group{
    packName:string;
    countIndex = 0;
    packArray:({[k:string]:any})[] = reactive([]);

    constructor(packName=""){
        super();
        this.packName = packName;
    }

    addEmptyFun(){
        const f = new FunGroup("fun"+this.countIndex++);
        this.packArray.push(f);
    }

    addFun(fun:FunGroup | ObjectType){
        
        if(fun instanceof FunGroup){
            if(this.isName(fun.getFunName())) return false;
            this.packArray.push(fun);
            return true;
        }
        else{
            if(this.isName(fun.name)) return false;
            const f = new FunGroup(fun.name);
            f.setData(fun);
            this.packArray.push(f);
            return true;
        }
    }

    addEmptyClass(){
        const f = new ClassGroup("class"+this.countIndex++);
        this.packArray.push(f);
    }

    addClass(fun:ClassGroup | ObjectType){
        if(fun instanceof ClassGroup){
            if(this.isName(fun.getClassName())) return false;
            this.packArray.push(fun);
            return true;
        }
        else{
            if(this.isName(fun.name)) return false;
            const f = new ClassGroup(fun.name);
            f.setData(fun);
            this.packArray.push(f);
            return true;
        }
    }

    addProp(){return}

    isName(name:string){
        for (let i = 0; i < this.packArray.length; i++) {
            const f = this.packArray[i];
           if( f instanceof FunGroup && f.getFunName() === name) return true;
           else if( f instanceof ClassGroup && f.getClassName() === name) return true;
        }
        return false;
    }

    setData(data:ObjectType){
        this.packName = data.name;
        this.countIndex = data.countIndex;
        if(data.funs){
            data.funs.forEach((item:ObjectType)=>{
                this.addFun(item);
            });
        }
        if(data.classes){
            data.classes.forEach((item:ObjectType)=>{
                this.addClass(item);
            });
        }
    }

    getData(){
        this.resave();
        const data:ObjectType = {
            type:"class",
            name: this.packName,
            countIndex: this.countIndex,
            classes: [],
            funs: [],
            props:[],
        };

        const packArray = toRaw(this.packArray);
        packArray.forEach(item=>{
            if(item instanceof FunGroup){
                data.funs.push(item.getData());
            }
            else if(item instanceof ClassGroup){
                data.classes.push(item.getData());
            }
        })
        
        return data;
    }

    setPackName(name:string){
        this.packName = name;
    }

    resave(){
        this.packArray.forEach(item=>{
            item.resave();
        })
    }

    getPackName(){
        return this.packName;
    }

    toString(): string {
        const arr = toRaw(this.packArray);
        let code = "";
        arr.forEach(item=>{
            if(item instanceof ClassGroup){
                code += `\nexport ` + item.toString();
            }
            else if(item instanceof FunGroup){
                code += `\nexport ` + item.toString();
            }
        })
        return code;
    }

    toStringProduce(): string {
        const arr = toRaw(this.packArray);
        let code = `\nconst ${this.packName} = {`;
        arr.forEach((item,i)=>{
            if(i > 0) code += ",";
            if(item instanceof ClassGroup){
                code += ` ${item.className}:` + item.toString();
            }
            else if(item instanceof FunGroup){
                item.setMode("");
                code += ` ${item.getFunName()}:` + item.toString();
            }
        })

        return code + "}";
    }
}

export class ProjectGroup extends Group{
    projectName:string;
    projectArray:({[k:string]:any})[] = reactive([]);
    countIndex = 0;
    constructor(projectName=""){
        super();
        this.projectName = projectName;
    }

    addEmptyPack(){
        const f = new PackGroup("pack"+this.countIndex++);
        this.projectArray.push(f);
        return f;
    }

    addPack(pack: PackGroup | ObjectType){
        
        if(pack instanceof PackGroup){
            if(this.isName(pack.getPackName())) return false;
            this.projectArray.push(pack);
            return true;
        }
        else{
            if(this.isName(pack.name)) return false;
            const f = new PackGroup(pack.name);
            f.setData(pack);
            this.projectArray.push(f);
            return true;
        }
    }

    isName(name:string){
        for (let i = 0; i < this.projectArray.length; i++) {
            const f = this.projectArray[i];
           if( f instanceof PackGroup && f.getPackName() === name) return true;
        }
        return false;
    }

    setData(data:ObjectType){ 
        if(data.packs){
            this.projectName = data.name;
            this.countIndex = data.countIndex || 0;
            data.packs.forEach((item:ObjectType)=>{
                this.addPack(item);
            });
        }
    }

    getData(){
        this.resave();
        const data:ObjectType = {
            type:"project",
            name: this.projectName,
            countIndex: this.countIndex,
            packs: [],
        };
        const projectArray = toRaw(this.projectArray);
        projectArray.forEach(item=>{
            data.packs.push(item.getData());
        })
        
        return data;
    }

    setProjectName(name:string){
        this.projectName = name;
    }

    resave(){
        this.projectArray.forEach(item=>{
            item.resave();
        })
    }

    files() {
        const arr = toRaw(this.projectArray);
        
        const files:ObjectType = {};

        arr.forEach(item=>{
            if(item instanceof PackGroup){
                files[item.getPackName()] = item.toString(); 
            }
        })

        return files;
    }

    toStringProduce() {
        const arr = toRaw(this.projectArray);
        let code = "";
        arr.forEach(item=>{
            if(item instanceof PackGroup){
                code +=  item.toStringProduce();
            }
        })

        return code;
    }
}